use std::fmt::Display;

use crate::{database::query_string_value, DrillDown};
use serde::{Deserialize, Deserializer, Serialize};
use shq_common::prelude::{
    Align, ColumnCell, ColumnCellBranch, ColumnCellLeaf, ColumnRange, DataType, FliterItems, parse_exp, report::{ParamSelected, Unit}, table_props::TableProps
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(untagged)]
pub enum LowCardinality {
    Asc,
    Desc,
    Tops(Vec<String>),
}
impl Default for LowCardinality {
    fn default() -> Self {
        LowCardinality::Asc
    }
}
impl <'de> Deserialize<'de> for LowCardinality {
    fn deserialize<D>(deserializer: D) -> Result<LowCardinality, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        let v = match s.to_lowercase().as_str() {
            "asc" | "true" => LowCardinality::Asc,
            "desc" => LowCardinality::Desc,
            _ => {
                let items: Vec<String> = s
                    .split(',')
                    .map(|x| x.trim().to_string())
                    .filter(|x| !x.is_empty())
                    .collect();
                LowCardinality::Tops(items)
            }
        };
        Ok(v)
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Column {
    #[serde(default)]
    pub name: Option<String>,
    #[serde(default)]
    pub title: Option<String>,
    /** 查看列所需要的权限类型，用于根据权限控制用户是否可以查看 */
    #[serde(default)]
    pub popedom_types: Vec<ColumnRange>,
    #[serde(default)]
    pub display: bool,
    #[serde(default)]
    pub front_must: bool,
    #[serde(default = "default_width")]
    pub width: u16,
    #[serde(default)]
    pub col_exp: Option<String>,
    #[serde(default)]
    pub data_type: DataType,
    #[serde(default)]
    pub format: Option<String>,
    #[serde(default)]
    pub sorter: bool,
    #[serde(default)]
    pub filter: bool,
    #[serde(default)]
    pub ellipsis: bool,
    #[serde(default)]
    pub fixed: bool,
    #[serde(default)]
    pub align: Align,
    #[serde(default)]
    pub unit: Option<Unit>,
    //分组计算方式
    #[serde(default)]
    pub group_calc: GroupCalc,
    //列上的合计计算方式
    pub col_total_calc: Option<GroupCalc>,
    //行上的合计计算方式
    pub row_total_calc: Option<GroupCalc>,
    //子列
    #[serde(default)]
    pub children: Vec<Column>,
    // 是否低基数列,如果是低基数列，则在前端筛选时以下拉方式选择
    #[serde(default)]
    pub low_cardinality: Option<LowCardinality>,
    // 默认筛选条件
    #[serde(default)]
    pub default_filter: Option<FliterItems>,
    #[serde(default)]
    pub param_selected: Option<ParamSelected>,
}
impl Column {
    pub fn new(
        name: Option<String>,
        title: Option<String>,
        col_exp: Option<String>,
        group_calc: GroupCalc,
        align: Align,
        fixed: bool,
        unit: Option<Unit>,
        children: Vec<Column>,
        col_total_calc: Option<GroupCalc>,
    ) -> Self {
        Column {
            name,
            title,
            popedom_types: vec![],
            display: true,
            width: default_width(),
            col_exp,
            data_type: DataType::String,
            format: None,
            sorter: true,
            filter: true,
            ellipsis: false,
            fixed,
            align,
            unit,
            group_calc: group_calc,
            col_total_calc: col_total_calc,
            row_total_calc: None,
            children,
            front_must: false,
            low_cardinality: None,
            default_filter: None,
            param_selected: None,
        }
    }
    pub async fn to_column_cell(
        &self,
        drill_downs: &[DrillDown],
        params: &[(&str, &str)],
    ) -> anyhow::Result<ColumnCell> {
        let cell = if self.children.len() > 0 {
            let b = to_rept_column_branch(self, drill_downs, params).await?;
            ColumnCell::Branch(b)
        } else {
            let l = to_column_cell_leaf(self, params, drill_downs).await?;
            ColumnCell::Leaf(l)
        };
        Ok(cell)
    }
}
pub async fn to_rept_column_branch(
    col: &Column,
    drill_downs: &[DrillDown],
    params: &[(&str, &str)],
) -> anyhow::Result<ColumnCellBranch> {
    let mut children: Vec<ColumnCell> = Vec::new();
    for c in col.children.iter() {
        let cell = Box::pin(c.to_column_cell(drill_downs, params)).await?;
        children.push(cell);
    }
    let title = if let Some(title) = col.title.as_ref() {
        Some(parse_title(title, params).await?)
    } else {
        None
    }
    .unwrap();
    let b = ColumnCellBranch {
        title: title,
        colspan: 1,
        rowspan: 1,
        children,
    };
    Ok(b)
}
pub async fn to_column_cell_leaf(
    col: &Column,
    params: &[(&str, &str)],
    drill_downs: &[DrillDown],
) -> anyhow::Result<ColumnCellLeaf> {
    let mut format = col.format.clone();
    if format.is_none() {
        match col.data_type {
            DataType::Float => format = Some(".2n".to_string()),
            _ => format = None,
        }
    }
    let title = if let Some(title) = col.title.as_ref() {
        Some(parse_title(title, params).await?)
    } else {
        None
    };
    let leaf = ColumnCellLeaf {
        name: col.name.clone().unwrap(),
        title: title,
        data_type: col.data_type,
        colspan: 1,
        rowspan: 1,
        list_props: Some(TableProps {
            display: col.display,
            filter: col.filter,
            sorter: col.sorter,
            ellipsis: col.ellipsis,
            edit: false,
            fixed: col.fixed,
            align: col.align.clone(),
            width: col.width,
            format,
        }),
        dict_col_name: None,
        front_must: col.front_must,
        is_null: false,
        default_filter: col.default_filter.clone(),
        param_selected: col.param_selected.clone(),
    };
    Ok(leaf)
}
fn default_width() -> u16 {
    140
}
impl Column {
    pub(crate) fn get_leaf_columns(&self) -> Vec<&Column> {
        if self.children.len() == 0 {
            return vec![self];
        }
        let mut leaf_columns = vec![];
        for c in self.children.iter() {
            if c.children.len() > 0 {
                leaf_columns.extend(c.get_leaf_columns());
                continue;
            } else {
                leaf_columns.push(c);
            }
        }
        leaf_columns
    }
    pub(super) fn set_leaf_columns_type(
        &mut self,
        col_types: &Vec<(String, DataType)>,
        unit: Unit,
    ) -> anyhow::Result<()> {
        if self.children.len() > 0 {
            for c in self.children.iter_mut() {
                c.set_leaf_columns_type(&col_types, unit)?;
            }
        } else {
            col_types
                .iter()
                .find(|(k, _)| Some(k.as_str()) == self.name.as_deref())
                .map(|(_, v)| {
                    self.data_type = v.clone();
                    self.parse(unit)
                });
        }
        Ok(())
    }
    pub(super) async fn parse(&mut self, rept_unit: Unit) -> anyhow::Result<()> {
        if self.display && self.title.is_none() {
            anyhow::bail!("display为true时，title不能为None")
        }
        let clen = self.children.len();
        if clen > 0 {
            if self.title.is_none() {
                anyhow::bail!("children的长度为{}时，title不能为None", clen);
            }
        } else {
            if self.name.is_none() {
                anyhow::bail!("children的长度为{}时，name不能为None", clen);
            }
        }
        if clen > 0 {
            return Ok(());
        }
        if self.unit.is_none() {
            self.unit = Some(rept_unit);
        }
        let name = self.name.as_ref().unwrap();
        if let Some(fmt) = self.format.as_ref() {
            let res = match self.data_type {
                DataType::Bool => Err(anyhow::anyhow!(
                    "column:{}的类型为bool，无需设置format",
                    name
                )),
                DataType::String => {
                    anyhow::bail!("column:{}的类型为String，无需设置format", name)
                }
                DataType::Date => {
                    if fmt.contains("%Y") || fmt.contains("%m") || fmt.contains("%d") {
                        Ok(())
                    } else {
                        anyhow::bail!("column:{}的类型为Date，format使用%Y-%m-%d的形式", name)
                    }
                }
                DataType::Datetime => {
                    if fmt.contains("%Y")
                        || fmt.contains("%m")
                        || fmt.contains("%d")
                        || fmt.contains("%H")
                        || fmt.contains("%M")
                        || fmt.contains("%S")
                    {
                        Ok(())
                    } else {
                        anyhow::bail!(
                            "column:{}的类型为DateTime，format使用%Y-%m-%d %H:%M:%S的形式",
                            name
                        )
                    }
                }
                DataType::Int | DataType::Float => {
                    if fmt.starts_with(".") || fmt.ends_with("n") || fmt.ends_with("%") {
                        Ok(())
                    } else {
                        Err(anyhow::anyhow!(
                            "column:{}的类型为Int或Float，format必须以.开头,以n或者%结尾",
                            name
                        ))
                    }
                }
            };
            res?;
        }
        if let Some(param_selected) = self.param_selected.as_ref() {
            if param_selected.multiple {
                if self.data_type != DataType::String {
                    anyhow::bail!(
                        "column:{}的param_selected为多选时，data_type必须为String类型",
                        name
                    );
                }
            }
            if self.low_cardinality.is_none() {
                anyhow::bail!("列{}设置了param_selected，low_cardinality必须不为None", name);
            }
        }
        Ok(())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum GroupCalc {
    Acbl,
    Sum,
    Avg,
    Max,
    Min,
    Count,
    Any,
    None,
    Str(String),
}
impl GroupCalc {
    pub fn to_str(&self) -> &'static str {
        match self {
            GroupCalc::Acbl => "最后一个",
            GroupCalc::Sum => "合计",
            GroupCalc::Avg => "平均",
            GroupCalc::Max => "最大",
            GroupCalc::Min => "最小",
            GroupCalc::Count => "计数",
            GroupCalc::Any => "任意一个",
            GroupCalc::None => "无",
            GroupCalc::Str(_) => "自定义",
        }
    }
}
impl Default for GroupCalc {
    fn default() -> Self {
        GroupCalc::Any
    }
}
impl Display for GroupCalc {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            GroupCalc::Acbl => write!(f, "anyLast"),
            GroupCalc::Sum => write!(f, "sum"),
            GroupCalc::Avg => write!(f, "avg"),
            GroupCalc::Max => write!(f, "max"),
            GroupCalc::Min => write!(f, "min"),
            GroupCalc::Count => write!(f, "count"),
            GroupCalc::Any => write!(f, "any"),
            GroupCalc::None => write!(f, ""),
            GroupCalc::Str(s) => write!(f, "{}", s),
        }
    }
}
impl<'de> Deserialize<'de> for GroupCalc {
    fn deserialize<D>(deserializer: D) -> Result<GroupCalc, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        let v = match s.to_lowercase().as_str() {
            "acbl" => GroupCalc::Acbl,
            "sum" => GroupCalc::Sum,
            "avg" => GroupCalc::Avg,
            "max" => GroupCalc::Max,
            "min" => GroupCalc::Min,
            "count" => GroupCalc::Count,
            "any" => GroupCalc::Any,
            "none" => GroupCalc::None,
            _ => GroupCalc::Str(s),
        };
        Ok(v)
    }
}
async fn parse_title(title: &str, params: &[(&str, &str)]) -> anyhow::Result<String> {
    let title = title.trim();
    let title = if title.starts_with("select ") || title.starts_with("SELECT ") {
        let sql = parse_exp(&title, params)?;
        let title = query_string_value(&sql).await?;
        title
    } else {
        title.to_string()
    };
    Ok(title)
}

